﻿using System;
using System.Collections.Generic;
using System.Configuration;
using UserSessionService.WebApi.Models;

using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Table;
using Microsoft.WindowsAzure.Storage.RetryPolicies;


namespace UserSessionService.WebApi.DataLayer
{
    public class AzureTablesRepository : IUserSessionRepository
    {
        CloudTableClient tableClient;
        CloudTable table;
        List<ViewedItem> viewedItems;
        string tableName = "UserSession";

        public AzureTablesRepository()
        {
            tableName = Helper.GetAppSetting("AzureTableNameForUserSession");
            viewedItems = new List<ViewedItem>();

            var storageConnectionString = Helper.GetConnectionString("StorageConnectionString");
            var storageAccount = storageConnectionString=="UseDevelopmentStorage=true"
                ? CloudStorageAccount.DevelopmentStorageAccount
                : CloudStorageAccount.Parse(storageConnectionString);
            
            tableClient = storageAccount.CreateCloudTableClient();
            table = tableClient.GetTableReference(tableName);
            table.CreateIfNotExists();
        }

        public UserSession GetUserSession(string userId)
        {
            string partitionFilter = TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, userId);
            TableQuery<ViewedItem> query = new TableQuery<ViewedItem>().Where(partitionFilter);
            
            var sessionItems = new List<string>();
            foreach (ViewedItem item in table.ExecuteQuery(query, new TableRequestOptions { RetryPolicy = new NoRetry() }))
                sessionItems.Add(item.ProductId);
            
            return new UserSession { UserId = userId, ViewedItems = sessionItems };
        }

        public List<string> GetViewedItems(string userId, int count)
        {
            List<string> items = null;

            var session = GetUserSession(userId);

            if (session != null && session.ViewedItems.Count > 0)
            {
                items = new List<string>();
                foreach (var viewedItem in session.ViewedItems)
                {
                    // Only want to return distinct list (no duplicates).
                    if (!items.Contains(viewedItem))
                        items.Add(viewedItem);
                    if (items.Count == count)
                        break;
                }                
            }

            return items;
        }

        public void AddViewedItem(string userId, string productId)
        {
            ViewedItem newViewedItem = null;
            if (!String.IsNullOrEmpty(userId) && !String.IsNullOrEmpty(productId))
            {
                newViewedItem = new ViewedItem(userId, productId);
                TableOperation insertOperation = TableOperation.Insert(newViewedItem);
                table.Execute(insertOperation);
            }
        }

        public void DeleteViewedItems()
        {
            table.DeleteIfExists();

            // Even though this is a synchronous call, the delete operation in Azure storage
            // is asynchronous.  So, if a table has alot of rows, it could take a while for the 
            // delete to complete.  This wouldn't be a problem except that if you later try
            // to create the table, you will get a HTTP 409 (conflict) because the delete operation
            // is still pending in Azure storage.
            //
            // As a workaround to this, try recreating the table here after we delete it.
            // Technically, the epxectation is an empty table.  So, go ahead and recreate it
            // here and poll in a loop until successful.
            // Supposedly this can take up to 40 seconds (maybe more in extreme cases).
            int numRetries = 10;
            do
            {
                numRetries--;

                try
                {
                    table.CreateIfNotExists();
                    break;   // Break out of this hack...
                }
                catch (StorageException e)
                {
                    if (e.RequestInformation.HttpStatusCode == 409)
                        System.Threading.Thread.Sleep(4000);
                }
            } while (numRetries > 0);
        }
    }
}